import { useMemo } from 'react'
import { debounce } from '@/library/libs/debounce'
import { IS_DEV } from '@/library/tool-kits/get-process-env'
import { isFunction } from '@/library/tool-kits/is-valid'
import { useLatest } from './use-latest'
import { useUnmount } from './use-unmount'

type noop = (...args: any[]) => any;

export interface DebounceOptions {
  wait?: number;
  leading?: boolean;
  trailing?: boolean;
  maxWait?: number;
}

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export function useDebounceFn<T extends noop>(fn: T, options?: DebounceOptions) {
  if (IS_DEV && !isFunction(fn)) {
    console.error(`useDebounceFn expected parameter is a function, got ${typeof fn}`)
  }

  const fnRef = useLatest(fn)

  const wait = options?.wait ?? 1000

  const debounced = useMemo(
    () => debounce(
      (...args: Parameters<T>): ReturnType<T> => fnRef.current(...args),
      wait,
      options,
    ),
    [],
  )

  useUnmount(() => {
    debounced.cancel()
  })

  return {
    run: debounced,
    cancel: debounced.cancel,
    flush: debounced.flush,
  }
}
